#include "stinger.h"
#include "build_type.h"
#include "stinger_vertex.h"



bool insert_edge(std::string src_vertex_name, std::string src_vertex_type, std::string dst_vertex_name, std::string dst_vertex_type, long timeFirst, long timeRecent, std::string edge_type_, int edge_weight) {
    /*
    Possibilities:
    1: Edge already exists and only needs updated.
    2: Edge does not exist, fits in an existing block.
    3: Edge does not exist, needs a new block.
    */

    

    /*将name转化为phy_id*/
    int src_vertex_phy_id = name_to_phy_id(src_vertex_type + "_" + src_vertex_name);
    int dst_vertex_phy_id = name_to_phy_id(dst_vertex_type + "_" + dst_vertex_name);


    /*首先搜索 mapper 看这俩个顶点是否都存在*/
    int src_logical_id = phy_id_to_logical_id(src_vertex_phy_id);
    if (src_logical_id == -1) {
        // 该顶点不存在，插入该顶点，设置出度为1，入度为0
        int insert_res1 = insert_vertex(src_vertex_name, src_vertex_type, 1, 0, 1);
        assert(insert_res1 == 1);
    } 

    int dst_logical_id = phy_id_to_logical_id(dst_vertex_phy_id);
    if (dst_logical_id == -1) {
        // 该顶点不存在，插入该顶点，设置出度为0，入度为1
        int insert_res2 = insert_vertex(dst_vertex_name, dst_vertex_type, 1, 1, 0);
        assert(insert_res2 == 1);
    } 

    

    struct stinger_vertex *vertex = get_vertex_by_phy_id(src_vertex_phy_id);
    //找到边类型的映射
    int edge_type = get_edge_type_mapping(edge_type_);

    /*获取源点的边块列表，遍历，判断边是否已经存在*/
    pmem::obj::persistent_ptr<stinger_eb> edge_block_ptr = vertex->edge_block_ptr;

    while (1) {
        if (edge_block_ptr == nullptr) {
            break;
        } else {
            if (edge_block_ptr->etype == edge_type) {
                for (int i = 0; i < edge_block_ptr->high; i++) {
                    if (edge_block_ptr->edges[i].neighbor == dst_vertex_phy_id) {
                        //找到了这条边，直接更新这条边
                        edge_block_ptr->edges[i].weight.get_rw() = edge_weight;
                        //edge_block_ptr->edges[i].timeFirst.get_rw() = timeFirst;
                        edge_block_ptr->edges[i].timeRecent.get_rw() = timeRecent;
                        edge_block_ptr->largeStamp.get_rw() = timeRecent;
                        return true;
                    }
                }
            }
            edge_block_ptr = edge_block_ptr->next_edge_block_ptr;
            
        }
    }

    if (src_logical_id != -1) {
        /*修改俩个顶点的入度和出度*/
        struct stinger_vertex *src_vertex = get_vertex_by_phy_id(src_vertex_phy_id);
        assert(src_vertex != nullptr);
        src_vertex->outDegree.get_rw() = src_vertex->outDegree + 1;
    }
    if (dst_logical_id != -1) {
        //如果边已经存在，不需要修改顶点的入度，出度
        /*修改俩个顶点的入度和出度*/
        struct stinger_vertex *dst_vertex = get_vertex_by_phy_id(dst_vertex_phy_id);
        assert(dst_vertex != nullptr);
        dst_vertex->inDegree.get_rw() = dst_vertex->inDegree + 1;
    }


    /*遍历，找到一个空槽，插入*/
    edge_block_ptr = vertex->edge_block_ptr;
    //设置一个指针指向最后一个非空指针的边块
    pmem::obj::persistent_ptr<stinger_eb> last_eb_ptr = nullptr;

    bool alloc_eb = true; //是否需要重新分配一个边块
    while (1) {
        if (edge_block_ptr == nullptr) {
            //没有找到空插槽，需要重新分配
            break;
        } else {
            if (edge_block_ptr->etype == edge_type) {
                //必须在同类型的边块中找到空槽插入
                for (int i = 0; i < edge_block_ptr->high; i++) {
                    if (edge_block_ptr->edges[i].neighbor < 0) {
                        //邻接点的id小于0，表示这个边是无效边。
                        edge_block_ptr->edges[i].neighbor.get_rw() = dst_vertex_phy_id;
                        edge_block_ptr->edges[i].weight.get_rw() = edge_weight;
                        edge_block_ptr->edges[i].timeFirst.get_rw() = timeFirst;
                        edge_block_ptr->edges[i].timeRecent.get_rw() = timeRecent;
                        edge_block_ptr->numEdges.get_rw() = edge_block_ptr->numEdges + 1;
                        edge_block_ptr->largeStamp.get_rw() = timeRecent;
                        alloc_eb = false;
                        break;
                    }
                }
                //如果空插槽没有了，那么判断是否还有其他没有用过的空间
                if (!alloc_eb) break;
                if (edge_block_ptr->high < STINGER_EDGEBLOCKSIZE) {

                    //还可以在这个边块里面继续插入
                    edge_block_ptr->edges[edge_block_ptr->high].neighbor.get_rw() = dst_vertex_phy_id;
                    edge_block_ptr->edges[edge_block_ptr->high].weight.get_rw() = edge_weight;
                    edge_block_ptr->edges[edge_block_ptr->high].timeFirst.get_rw() = timeFirst;
                    edge_block_ptr->edges[edge_block_ptr->high].timeRecent.get_rw() = timeRecent;
                    edge_block_ptr->numEdges.get_rw() = edge_block_ptr->numEdges + 1;
                    edge_block_ptr->high.get_rw() = edge_block_ptr->high + 1;
                    edge_block_ptr->largeStamp.get_rw() = timeRecent;
                    //High water mark?????
                    alloc_eb = false;
                    break;
                } else {
                    //当前的边块没有空间了
                    last_eb_ptr = edge_block_ptr;
                    edge_block_ptr = edge_block_ptr->next_edge_block_ptr;
                }
                
            } else {
                last_eb_ptr = edge_block_ptr;
                edge_block_ptr = edge_block_ptr->next_edge_block_ptr;
            }
            
        }
    }

    /*需要重新分配一个边块*/
    if (alloc_eb) {
        pmem::obj::transaction::run(pop, [&] {
            pmem::obj::persistent_ptr<stinger_eb> new_edge_block = pmem::obj::make_persistent<stinger_eb>();
            new_edge_block->edges[0].neighbor.get_rw() = dst_vertex_phy_id;
            new_edge_block->edges[0].weight.get_rw() = edge_weight;
            new_edge_block->edges[0].timeFirst.get_rw() = timeFirst;
            new_edge_block->edges[0].timeRecent.get_rw() = timeRecent;
            new_edge_block->etype.get_rw() = edge_type;
            new_edge_block->next_edge_block_ptr = nullptr;
            new_edge_block->vertexID.get_rw() = src_vertex_phy_id;
            new_edge_block->numEdges.get_rw() = 1;
            new_edge_block->high.get_rw() = 1;
            new_edge_block->smallStamp.get_rw() = timeFirst;
            new_edge_block->largeStamp.get_rw() = timeFirst;

            if (vertex->edge_block_ptr == nullptr) {
                vertex->edge_block_ptr = new_edge_block;
            } else {
                last_eb_ptr->next_edge_block_ptr = new_edge_block;
            }
        });

    }
    return true;

}


//根据俩个物理顶点id，获取一条边
stinger_edge* get_edge_by_phy_id(int src_phy_id, int dst_phy_id, int edge_type) {

    /*首先搜索 mapper 看这俩个顶点是否都存在*/
    int src_logical_id = phy_id_to_logical_id(src_phy_id);
    if (src_logical_id == -1) {
        return nullptr;
    }

    int dst_logical_id = phy_id_to_logical_id(dst_phy_id);
    if (dst_logical_id == -1) {
        return nullptr;
    }

    struct stinger_vertex *vertex = get_vertex_by_phy_id(src_phy_id);


    

    /*获取源点的边块列表，遍历，判断边是否已经存在*/
    pmem::obj::persistent_ptr<stinger_eb> edge_block_ptr = vertex->edge_block_ptr;
    
    
    while (1) {
        if (edge_block_ptr == nullptr) {
            break;
        } else {
            if (edge_block_ptr->etype == edge_type) {
                for (int i = 0; i < edge_block_ptr->high; i++) {
                    if (edge_block_ptr->edges[i].neighbor == dst_phy_id) {
                        //找到了这条边，返回这条边
                        return &edge_block_ptr->edges[i];
                    }
                }
            } 
            edge_block_ptr = edge_block_ptr->next_edge_block_ptr;
        }
    }
    return nullptr;
}


//根据俩个name，获取一条边
stinger_edge* get_edge_by_name(std::string src_vertex_name, std::string src_vertex_type, std::string dst_vertex_name, std::string dst_vertex_type, std::string edge_type_) {
    /*将name转化为phy_id*/
    int src_vertex_phy_id = name_to_phy_id(src_vertex_type + "_" + src_vertex_name);
    int dst_vertex_phy_id = name_to_phy_id(dst_vertex_type + "_" + dst_vertex_name);
    //找到边类型的映射
    int edge_type = get_edge_type_mapping(edge_type_);
    return get_edge_by_phy_id(src_vertex_phy_id, dst_vertex_phy_id, edge_type);
}

/*
删除一条边
如果边不存在，返回0
如果删除成功，返回1
*/

int delete_edge_by_phy_id(int src_phy_id, int dst_phy_id, int edge_type) {
    struct stinger_edge *edge = get_edge_by_phy_id(src_phy_id, dst_phy_id, edge_type);
    if (edge == nullptr) {
        /*这条边不存在*/
        return 0;
    } else {
        /*这条边存在*/
        struct stinger_vertex *vertex = get_vertex_by_phy_id(src_phy_id);
        pmem::obj::persistent_ptr<stinger_eb> edge_block_ptr = vertex->edge_block_ptr;

        while (1) {
            if (edge_block_ptr == nullptr) {
                break;
            } else {
                if (edge_block_ptr->etype == edge_type) {
                    for (int i = 0; i < edge_block_ptr->high; i++) {
                        if (edge_block_ptr->edges[i].neighbor == dst_phy_id) {
                            //找到了这条边，将目的顶点的id设置为负数，表示将其无效掉
                            edge_block_ptr->edges[i].neighbor.get_rw() = -1;
                            edge_block_ptr->numEdges.get_rw() = edge_block_ptr->numEdges - 1;
                            return 1;
                        }
                    }
                }
                edge_block_ptr = edge_block_ptr->next_edge_block_ptr;
            }
        }
        return 1;
    }
}

int delete_edge_by_name(std::string src_vertex_name, std::string src_vertex_type, std::string dst_vertex_name, std::string dst_vertex_type, std::string edge_type_) {
    /*将边的name转化为phy_id*/
    int src_vertex_phy_id = name_to_phy_id(src_vertex_type + "_" + src_vertex_name);
    int dst_vertex_phy_id = name_to_phy_id(dst_vertex_type + "_" + dst_vertex_name);

    /*将边的类型名转为id*/
    int edge_type = get_edge_type_mapping(edge_type_);
    return delete_edge_by_phy_id(src_vertex_phy_id, dst_vertex_phy_id, edge_type);

}


